"""
    PCF8563 RTC drive
    Author: Maker Sun base on shaoziyang 2021.1 version
    Date:   2024.9.9
    http://www.micropython.org.cn
"""

from micropython import const

PCF8563_I2C_ADDRESS  = const(81)
PCF8563_REG_CTRL1    = const(0)
PCF8563_REG_CTRL2    = const(1)
PCF8563_REG_SECOND   = const(2)
PCF8563_REG_MINUTE   = const(3)
PCF8563_REG_HOUR     = const(4)
PCF8563_REG_WEEKDAY  = const(6)
PCF8563_REG_DAY      = const(5)
PCF8563_REG_MONTH    = const(7)
PCF8563_REG_YEAR     = const(8)
PCF8563_REG_ALARM_MINUTE= const(9)
PCF8563_REG_ALARM_HOUR=const(10)
PCF8563_REG_ALARM_DAY=const(11)
PCF8563_REG_ALARM_WEEK=const(12)
PCF8563_REG_CTRL_TIMER=const(14)
PCF8563_REG_TIMEOUT=const(15)

PCF8563_REG_ALARM_ENABLE =const(0b1000_0000)

PCF8563_REG_CTRL2_TI_TP = const(0b0001_0000)
PCF8563_REG_CTRL2_AF = const(0b0000_1000)
PCF8563_REG_CTRL2_TF = const(0b0000_0100)
PCF8563_REG_CTRL2_AIE = const(0b0000_0010)
PCF8563_REG_CTRL2_TIE = const(0b0000_0001)

PCF8563_REG_CTRL_TIMER_FREQUENCY = const(0b0000_0011)
PCF8563_REG_CTRL_TIMER_TE = const(0b1000_0000)


class PCF8563():
    TIMER_FREQUENCY_4096 = 0
    TIMER_FREQUENCY_64 = 1
    TIMER_FREQUENCY_1 = 2
    TIMER_FREQUENCY_1_64 = 3
    
    def __init__(self, i2c):
        self.i2c = i2c
        self.tb = bytearray(1)
        self.rb = bytearray(1)
        self.buf = bytearray(7)
        self.DT = [0] * 8
        self.abuf = bytearray(4)
        self.AT = [0] * 4
    # set reg
    
    def	setReg(self, reg, dat):
        self.tb[0] = dat
        self.i2c.writeto_mem(PCF8563_I2C_ADDRESS, reg, self.tb)

    # get reg
    def	getReg(self, reg):
        self.i2c.readfrom_mem_into(PCF8563_I2C_ADDRESS, reg, self.rb)
        return self.rb[0]


    def DecToHex(self, dat):
        return (dat//10) * 16 + (dat%10)

    def HexToDec(self, dat):
        return (dat//16) * 10 + (dat%16)

    def year(self, year = None):
        if year == None:
            return self.HexToDec(self.getReg(PCF8563_REG_YEAR)) + 2000
        else:
            self.setReg(PCF8563_REG_YEAR, self.DecToHex(year%100))

    def month(self, month = None):
        if month == None:
            return self.HexToDec(self.getReg(PCF8563_REG_MONTH)%32)
        else:
            self.setReg(PCF8563_REG_MONTH, self.DecToHex(month%13))

    def day(self, day = None):
        if day == None:
            return self.HexToDec(self.getReg(PCF8563_REG_DAY)%64)
        else:
            self.setReg(PCF8563_REG_DAY, self.DecToHex(day%32))

    def weekday(self, weekday = None):
        if weekday == None:
            return self.HexToDec(self.getReg(PCF8563_REG_WEEKDAY)%8)
        else:
            self.setReg(PCF8563_REG_WEEKDAY, self.DecToHex(weekday%8))

    def hour(self, hour = None):
        if hour == None:
            return self.HexToDec(self.getReg(PCF8563_REG_HOUR)%64)
        else:
            self.setReg(PCF8563_REG_HOUR, self.DecToHex(hour%24))

    def minute(self, minute = None):
        if minute == None:
            return self.HexToDec(self.getReg(PCF8563_REG_MINUTE)%128)
        else:
            self.setReg(PCF8563_REG_MINUTE, self.DecToHex(minute%60))

    def second(self, second = None):
        if second == None:
            return self.HexToDec(self.getReg(PCF8563_REG_SECOND)%128)
        else:
            self.setReg(PCF8563_REG_SECOND, self.DecToHex(second%60))


    def datetime(self, DT=None):
        if DT == None:
            self.i2c.readfrom_mem_into(PCF8563_I2C_ADDRESS, PCF8563_REG_SECOND, self.buf)
            self.DT[0] = self.HexToDec(self.buf[6]) + 2000
            self.DT[1] = self.HexToDec(self.buf[5]%32)
            self.DT[2] = self.HexToDec(self.buf[3]%64)
            self.DT[3] = self.HexToDec(self.buf[4]%8)
            self.DT[4] = self.HexToDec(self.buf[2]%64)
            self.DT[5] = self.HexToDec(self.buf[1]%128)
            self.DT[6] = self.HexToDec(self.buf[0]%128)
            self.DT[7] = 0
            return self.DT
            
        else:
            self.buf[0] = self.DecToHex(DT[6]%60)    # second
            self.buf[1] = self.DecToHex(DT[5]%60)    # minute
            self.buf[2] = self.DecToHex(DT[4]%24)    # hour
            self.buf[3] = self.DecToHex(DT[2]%32)    # date
            self.buf[4] = self.DecToHex(DT[3]%8)     # week da
            self.buf[5] = self.DecToHex(DT[1]%13)    # month
            self.buf[6] = self.DecToHex(DT[0]%100)   # year
            self.i2c.writeto_mem(PCF8563_I2C_ADDRESS, PCF8563_REG_SECOND, self.buf)
    
    def alarm_week(self, week=None):
        #获取或设置星期闹钟，-1：清除星期定时器，0~6：设置星期并启用星期闹钟
        if week == None:
            if self.getReg(PCF8563_REG_ALARM_WEEK)&PCF8563_REG_ALARM_ENABLE==0:
                return self.HexToDec(self.getReg(PCF8563_REG_ALARM_WEEK)%128)
            else:
                return None
        else:
            if week>=0:
                self.setReg(PCF8563_REG_ALARM_WEEK, self.DecToHex(week%7))
            else:
                self.setReg(PCF8563_REG_ALARM_WEEK, PCF8563_REG_ALARM_ENABLE)
    
    def alarm_day(self, day=None):
        #获取或设置日闹钟，-1：清除日定时器，1~31：设置日期并启用日期闹钟
        if day == None:
            if self.getReg(PCF8563_REG_ALARM_DAY)&PCF8563_REG_ALARM_ENABLE==0:
                return self.HexToDec(self.getReg(PCF8563_REG_ALARM_DAY)%128)
            else:
                return None
        else:
            if day>=0:
                self.setReg(PCF8563_REG_ALARM_DAY, self.DecToHex(day%32))
            else:
                self.setReg(PCF8563_REG_ALARM_DAY, PCF8563_REG_ALARM_ENABLE)
    
    def alarm_hour(self, hour=None):
        #获取或设置小时闹钟，-1：清除小时闹钟，0~23：设置小时数并启用小时闹钟
        #get or set alarm hour or -1 clean AE Flag 
        if hour == None:
            if self.getReg(PCF8563_REG_ALARM_HOUR)&PCF8563_REG_ALARM_ENABLE==0:
                return self.HexToDec(self.getReg(PCF8563_REG_ALARM_HOUR)%128)
            return None
        else:
            if hour>=0:
                self.setReg(PCF8563_REG_ALARM_HOUR, self.DecToHex(hour%24))
            else:
                self.setReg(PCF8563_REG_ALARM_HOUR, PCF8563_REG_ALARM_ENABLE)
    
    def alarm_minute(self, minute=None):
        #获取或设置分钟闹钟，0：清除分钟定时器，1~60：设置分钟数并启用分钟闹钟
        # get or set alarm minute
        if minute == None:
            if self.getReg(PCF8563_REG_ALARM_MINUTE)&PCF8563_REG_ALARM_ENABLE==0:
                return self.HexToDec(self.getReg(PCF8563_REG_ALARM_MINUTE)%128)
            else:
                return None
        else:
            if minute>=0:
                self.setReg(PCF8563_REG_ALARM_MINUTE, self.DecToHex(minute%60))
            else:
                self.setReg(PCF8563_REG_ALARM_MINUTE, PCF8563_REG_ALARM_ENABLE)
    
    def alarm(self, AT=None):
        #获取或设置闹钟，AT是一个字节数组bytearray[3]
        #get or set alarm time, AT: bytearray[3]
        if AT == None:
            self.i2c.readfrom_mem_into(PCF8563_I2C_ADDRESS, PCF8563_REG_ALARM_MINUTE, self.abuf)
            self.AT[0] = self.HexToDec(self.abuf[3]&127) if self.abuf[3]&PCF8563_REG_ALARM_ENABLE else None#周
            self.AT[1] = self.HexToDec(self.abuf[2]&127) if self.abuf[2]&PCF8563_REG_ALARM_ENABLE else None#日
            self.AT[2] = self.HexToDec(self.abuf[1]&127) if self.abuf[1]&PCF8563_REG_ALARM_ENABLE else None#小时
            self.AT[3] = self.HexToDec(self.abuf[0]&127) if self.abuf[0]&PCF8563_REG_ALARM_ENABLE else None#分钟
            return self.AT
        else:
            self.abuf[0] = self.DecToHex(AT[3]%60)  # minute
            self.abuf[1] = self.DecToHex(AT[2]%24)  # hour
            self.abuf[2] = self.DecToHex(AT[1]%32)  # day
            self.abuf[3] = self.DecToHex(AT[0]%7)   # week
            self.i2c.writeto_mem(PCF8563_I2C_ADDRESS, PCF8563_REG_ALARM_MINUTE, self.abuf)
    
    def alarm_flag(self, clear=None):
        #get or clear alarm flag
        #返回或清除报警器有效标志，返回True 有效，False无效，当中断触发时是否
        if clear:
            self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)&(~PCF8563_REG_CTRL2_AF))
        else:
            return self.getReg(PCF8563_REG_CTRL2) & PCF8563_REG_CTRL2_AF >0
                
    
    def timer_timeout(self, timeout=None):
        #get or set timer tickcount value， 1~255
        #获取或设置定时器的计数器值：根据设置的定时器频率，意味着定时器可定时的范围是1/4096秒(≈244.140625微秒）到255*60秒（255分钟）之间
        #定时器启动后是循环的，也就是倒计时到0以后，会自动重新开始，TF标志位不会自动复位，需要手动复位
        if timeout==None:
            return self.getReg(PCF8563_REG_TIMEOUT)
        else:
            self.setReg(PCF8563_REG_TIMEOUT, timeout)
    
    def timer_enable(self, enable=None):
        #get or set timer whether enable
        #返回或设置定时器是否有效
        if enable==None:
            return self.getReg(PCF8563_REG_CTRL_TIMER) & PCF8563_REG_CTRL_TIMER_TE > 0
        else:
            if enable:
                self.setReg(PCF8563_REG_CTRL_TIMER, self.getReg(PCF8563_REG_CTRL_TIMER)|PCF8563_REG_CTRL_TIMER_TE)
            else:
                self.setReg(PCF8563_REG_CTRL_TIMER, self.getReg(PCF8563_REG_CTRL_TIMER)&(~PCF8563_REG_CTRL_TIMER_TE))
    
    def timer_frequency(self, frequency=None):
        #get or set timer frequency
        #获取或设置定时器的频率0: 4096Hz, 1:64Hz，2: 1Hz，3：1/60Hz
        if frequency==None:
            return self.getReg(PCF8563_REG_CTRL_TIMER)&PCF8563_REG_CTRL_TIMER_FREQUENCY
        else:
            self.setReg(PCF8563_REG_CTRL_TIMER, self.getReg(PCF8563_REG_CTRL_TIMER)&PCF8563_REG_CTRL_TIMER_TE|frequency)

    def timer_flag(self, clear=None):
        #get or clear timer flag(TF)
        #返回或清除定时器有效标志，返回True 有效，False无效，当产生中断信号时，用来判断是否是定时器中断
        if clear:
            self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)&(~PCF8563_REG_CTRL2_TF)&0b00011111)
        else:
            return self.getReg(PCF8563_REG_CTRL2) & PCF8563_REG_CTRL2_TF >0
        
    def timer_interupt(self, enable=None):
        #是否启用定时器中断，True启用，False不启用
        #get or set timer interupt enable
        if enable==None:
            return self.getReg(PCF8563_REG_CTRL2) & PCF8563_REG_CTRL2_TIE >0
        else:
            if enable:
                self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)|PCF8563_REG_CTRL2_TIE)
            else:
                self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)&(~PCF8563_REG_CTRL2_TIE)&0b00011111)
        
    def alarm_interupt(self, enable=None):
        #是否启用闹钟中断，True启用，False不启用
        #get or set alarm interupt enable
        if enable==None:
            return self.getReg(PCF8563_REG_CTRL2) & PCF8563_REG_CTRL2_AIE >0
        else:
            if enable:
                self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)|PCF8563_REG_CTRL2_AIE)
            else:
                self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)&(~PCF8563_REG_CTRL2_AIE)&0b00011111)
                
    def ti_tp_enable(self, enable=None):
        #设置是否输出脉冲信号，如果为true，则定时器触发时输出脉冲信号
        #get or set timer interupt output signle type
        if enable==None:
            return self.getReg(PCF8563_REG_CTRL2) & PCF8563_REG_CTRL2_TI_TP > 0
        else:
            if enable:
                self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)|PCF8563_REG_CTRL2_TI_TP)
            else:
                self.setReg(PCF8563_REG_CTRL2, self.getReg(PCF8563_REG_CTRL2)&(~PCF8563_REG_CTRL2_TI_TP)&0b00011111)
    
    def print_reg(self):
        buf = bytearray(16)
        for i in range(16):
            buf[i] = self.getReg(i)
            print(bin(buf[i]))
        return buf

    def print_timer_state(self):
        print("timer frequency:", self.timer_frequency())
        print("timer timeout:", self.timer_timeout())
        print("timer flag(TF):", self.timer_flag())
        print("timer enable(TE):", self.timer_enable())
        print("timer interrupt enable(TIE):", self.timer_interupt())
        print("ti/tp enable:", self.ti_tp_enable())
        
    def print_alarm_state(self):
        print("alarm week, day, hour, minute", self.alarm_week(), self.alarm_day(), self.alarm_hour(), self.alarm_minute())
        print("alarm flag(AF):", self.alarm_flag())
        print("alarm interrupt enable(AIE):", self.alarm_interupt())
        print("ti/tp enable:", self.ti_tp_enable())       